﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IMIForUnity;
using System.Runtime.InteropServices;
using UnityEngine;
namespace IMIForUnity
{
    public class ImiGestureManager
    {
        private Dictionary<System.Type, ImiGesture.OnGestureEvent> mainPlayerGestures;
        private Dictionary<System.Type, ImiGesture.OnGestureEvent> otherPlayerGestures;
        private Dictionary<ImiGesturePro.GestureType, ImiGesture.OnGestureEvent> mainPlayerGesturesPro;
        private Dictionary<ImiGesturePro.GestureType, ImiGesture.OnGestureEvent> otherPlayerGesturesPro;
        private DateTime BaseTime;
        private Dictionary<System.Type, ImiGesture.OnGestureEvent> gestureListeners;
        //private Dictionary<System.Type, ImiGesture.OnGestureStart> startListeners;
        //private Dictionary<System.Type, ImiGesture.OnGestureMoving> movingListeners;
        //private Dictionary<System.Type, ImiGesture.OnGestureCancel> cancelListeners;
        private static ImiGestureManager instance = null;
        private static object obj = new object();

        private static System.IntPtr recognizedPtr;
        private static System.IntPtr recognizingPtr;

        public ImiGestureManager()
        {
            mainPlayerGestures = new Dictionary<System.Type, ImiGesture.OnGestureEvent>();
            otherPlayerGestures = new Dictionary<System.Type, ImiGesture.OnGestureEvent>();
            mainPlayerGesturesPro = new Dictionary<ImiGesturePro.GestureType, ImiGesture.OnGestureEvent>();
            otherPlayerGesturesPro = new Dictionary<ImiGesturePro.GestureType, ImiGesture.OnGestureEvent>();

            BaseTime = new DateTime(1970, 1, 1, 0, 0, 0);
            gestureListeners = new Dictionary<Type, ImiGesture.OnGestureEvent>();

        }

        private void initGestueProCallBacks()
        {
            ImiGestureWrapper.ImiGestureRecognizedCallBack recognizedCallback = new ImiGestureWrapper.ImiGestureRecognizedCallBack(ImiGestureRecognizedCallBack);
            recognizedPtr = Marshal.GetFunctionPointerForDelegate(recognizedCallback);

            ImiGestureWrapper.ImiGestureRecognizingCallBack recognizingCallback = new ImiGestureWrapper.ImiGestureRecognizingCallBack(ImiGestureRecognizingCallBack);
            recognizingPtr = Marshal.GetFunctionPointerForDelegate(recognizingCallback);

        }


        private static void ImiGestureRecognizedCallBack(ImiGestureWrapper.ImiGestureEventArgs s)
        {
            //Debug.Log("native识别出的回调");
            ImiGesturePro pro = new ImiGesturePro(s.gestureType);
            ImiPlayerInfo player = ImiManager.GetInstance().GetPlayerInfo((int)(s.trackingID));

            pro.GestureEvent(pro, player, GestureEvent.Complete);

            if (player.playerGesturesPro[s.gestureType] != null)
            {
                player.playerGesturesPro[s.gestureType].Invoke(pro, player, GestureEvent.Complete);
            }
        }

        private static void ImiGestureRecognizingCallBack(ImiGestureWrapper.ImiGestureEventArgs s)
        {
            ImiGesturePro pro = new ImiGesturePro(s.gestureType);
            ImiPlayerInfo player = ImiManager.GetInstance().GetPlayerInfo((int)(s.trackingID));
            if (s.gestureData.progress == 0)
            {
                pro.GestureEvent(pro, player, GestureEvent.Moving);

                if (player.playerGesturesPro[s.gestureType] != null)
                {
                    player.playerGesturesPro[s.gestureType].Invoke(pro, player, GestureEvent.Moving);
                }
            }
            else
            {
                pro.GestureEvent(pro, player, GestureEvent.Complete);

                if (player.playerGesturesPro[s.gestureType] != null)
                {
                    player.playerGesturesPro[s.gestureType].Invoke(pro, player, GestureEvent.Complete);
                }
            }
        }


        public static ImiGestureManager GetInstance()
        {
            if (instance == null)
            {
                lock (obj)
                {
                    if (instance == null)
                    {
                        instance = new ImiGestureManager();
                    }
                }
            }
            return instance;
        }

        private void refresh()
        {
            //Debug.Log("Refreshing mainplayer gesture");
            foreach (KeyValuePair<System.Type, ImiGesture.OnGestureEvent> item in mainPlayerGestures)
            {
                //Debug.Log("Updating Gesture");
                updateMainPlayerGesture(item.Key, item.Value);
            }
            //Debug.Log("Refreshing subplayer gesture");
            foreach (KeyValuePair<System.Type, ImiGesture.OnGestureEvent> item in otherPlayerGestures)
            {
                updateSubPlayerGesture(item.Key, item.Value);
            }

            //foreach (KeyValuePair<ImiGesturePro.GestureType, ImiGesture.OnGestureEvent> item in mainPlayerGesturesPro)
            //{
            //    updateMainPlayerGesturePro(item.Key, item.Value);
            //}

            //foreach (KeyValuePair<ImiGesturePro.GestureType, ImiGesture.OnGestureEvent> item in otherPlayerGesturesPro)
            //{
            //    updateSubPlayerGesturePro(item.Key, item.Value);
            //}

        }

        public void Update()
        {
            //Debug.Log("Updating Gesture");
            refresh();
            //Debug.Log("Refreshing Gesture");
            CheckGestures();
        }

        //private void UpdateListeners(ImiPlayerInfo player, System.Type type)
        //{

        //    if (gestureListeners.ContainsKey(type))
        //    {
        //        player.playerGestures[type].GestureEventListeners = gestureListeners[type];
        //    }

        //}

        private int AddGesturePro(int userId, ImiGesturePro.GestureType type)
        {
            initGestueProCallBacks();
            return ImiGestureWrapper.ImiAddGesture((uint)userId, type, recognizedPtr, recognizingPtr);
        }

        public void AddGesture(System.Type gesture, ImiGesture.OnGestureEvent listener)
        {
            AddGestureToMainPlayer(gesture, listener);
            AddGestureToSubPlayer(gesture, listener);
        }

        public void AddGestureToMainPlayer(System.Type gesture, ImiGesture.OnGestureEvent listener)
        {
            if (!mainPlayerGestures.ContainsKey(gesture))
            {
                mainPlayerGestures.Add(gesture, new ImiGesture.OnGestureEvent(listener));
            }
        }


        public void AddGestureToSubPlayer(System.Type gesture, ImiGesture.OnGestureEvent listener)
        {
            if (!otherPlayerGestures.ContainsKey(gesture))
            {
                otherPlayerGestures.Add(gesture, new ImiGesture.OnGestureEvent(listener));
            }
        }

        public void AddGesturePro(ImiGesturePro.GestureType gestureType, ImiGesture.OnGestureEvent listener)
        {
            AddGestureToMainPlayerPro(gestureType, listener);
            AddGestureToSubPlayerPro(gestureType, listener);
        }

        public void AddGestureToMainPlayerPro(ImiGesturePro.GestureType gestureType, ImiGesture.OnGestureEvent listener)
        {
            if (!mainPlayerGesturesPro.ContainsKey(gestureType))
            {
                mainPlayerGesturesPro.Add(gestureType, new ImiGesture.OnGestureEvent(listener));
            }
        }

        public void AddGestureToSubPlayerPro(ImiGesturePro.GestureType gestureType, ImiGesture.OnGestureEvent listener)
        {
            if (!otherPlayerGesturesPro.ContainsKey(gestureType))
            {
                otherPlayerGesturesPro.Add(gestureType, new ImiGesture.OnGestureEvent(listener));
            }
        }

        public void RemoveGesture(System.Type gesture)
        {
            RemoveMainPlayerGesture(gesture);
            RemoveSubPlayerGesture(gesture);
        }

        public void RemoveMainPlayerGesture(System.Type gesture)
        {
            if (otherPlayerGestures.ContainsKey(gesture))
            {
                mainPlayerGestures.Remove(gesture);
            }
        }

        public void RemoveSubPlayerGesture(System.Type gesture)
        {
            if (otherPlayerGestures.ContainsKey(gesture))
            {
                otherPlayerGestures.Remove(gesture);
            }
        }

        public void RemoveGesturePro(ImiGesturePro.GestureType gestureType)
        {
            RemoveMainPlayerGesturePro(gestureType);
            RemoveSubPlayerGesturePro(gestureType);
        }

        public void RemoveMainPlayerGesturePro(ImiGesturePro.GestureType gestureType)
        {

            if (mainPlayerGesturesPro.ContainsKey(gestureType))
            {
                mainPlayerGesturesPro.Remove(gestureType);
            }

        }

        public void RemoveSubPlayerGesturePro(ImiGesturePro.GestureType gestureType)
        {
            if (otherPlayerGesturesPro.ContainsKey(gestureType))
            {
                otherPlayerGesturesPro.Remove(gestureType);
            }
        }

        public Dictionary<System.Type, ImiGesture.OnGestureEvent> GetMainPlayerGestures()
        {
            return mainPlayerGestures;
        }

        public Dictionary<ImiGesturePro.GestureType, ImiGesture.OnGestureEvent> getMainPlayerGesturesPro()
        {
            return mainPlayerGesturesPro;
        }

        public Dictionary<System.Type, ImiGesture.OnGestureEvent> GetOtherPlayerGestures()
        {
            return otherPlayerGestures;
        }

        public Dictionary<ImiGesturePro.GestureType, ImiGesture.OnGestureEvent> GetOtherPlayerGesturesPro()
        {
            return otherPlayerGesturesPro;
        }

        private void CheckGestures()
        {
            //Debug.Log("Checking Gestures");
            int mainPlayerIndex = ImiManager.GetInstance().GetControlPlayerId();
            //Debug.Log("MainPlayerIndex is " + mainPlayerIndex);
            Dictionary<int, ImiPlayerInfo> players = ImiManager.GetInstance().GetPlayerInfos();
            foreach (int key in players.Keys)
            {
                if (key == mainPlayerIndex)
                {
                    ImiPlayerInfo mainPlayer = ImiManager.GetInstance().GetPlayerInfo(mainPlayerIndex);
                    if (mainPlayer == null || !mainPlayer.GetPlayerTracked())
                        continue;
                    if (mainPlayer.playerGestures.Count > 0)
                    {
                        foreach (System.Type type in mainPlayerGestures.Keys)
                        {
                            checkGesture(mainPlayer, mainPlayer.playerGestures[type]);
                        }
                    }
                }
                else
                {
                    ImiPlayerInfo player = players[key];
                    if (player == null)
                        continue;
                    if (player.playerGestures.Count > 0)
                    {
                        foreach (System.Type type in otherPlayerGestures.Keys)
                        {
                            checkGesture(players[key], players[key].playerGestures[type]);
                        }
                    }
                }
            }
        }
        private void checkGesture(ImiPlayerInfo p, ImiGesture g)
        {
            if (p == null)
            {
                return;
            }
            ImiGesture.State state = g.CheckGesture(p, (DateTime.Now - BaseTime).TotalSeconds);
            if (state == ImiGesture.State.FINISHED)
            {

                g.GestureEvent(g, p, GestureEvent.Complete);

                if (g.GestureEventListeners != null)
                {
                    g.GestureEventListeners.Invoke(g, p, GestureEvent.Complete);
                }
                g.Reset();
            }
            else if (state == ImiGesture.State.CANCELED)
            {
                g.Reset();
            }
        }
        private void updateMainPlayerGesture(System.Type gesture, ImiGesture.OnGestureEvent listener)
        {
            int mainPlayerID = ImiManager.GetInstance().GetControlPlayerId();
            ImiPlayerInfo mainPlayer = ImiManager.GetInstance().GetPlayerInfo(mainPlayerID);
            //Debug.Log("trying to update main player");
            if (mainPlayer != null && mainPlayer.GetPlayerTracked())
            {
                //Debug.Log("Main Player Founded");
                if (!mainPlayer.playerGestures.ContainsKey(gesture))
                {
                    ImiGesture g = (ImiGesture)Activator.CreateInstance(gesture);
                    g.GestureEventListeners += listener;
                    mainPlayer.playerGestures.Add(gesture, g);
                }
                else
                {
                    mainPlayer.playerGestures[gesture].GestureEventListeners = listener;
                }
            }else
            {
                //Debug.Log("Main Player Not Founded");
            }
        }

        private void updateSubPlayerGesture(System.Type gesture, ImiGesture.OnGestureEvent listener)
        {
            Dictionary<int, ImiPlayerInfo> players = ImiManager.GetInstance().GetPlayerInfos();
            int mainPlayerID = ImiManager.GetInstance().GetControlPlayerId();
            if (players != null)
            {
                List<int> ids = new List<int>(players.Keys);
                foreach (int id in ids)
                {
                    if (id != mainPlayerID)
                    {
                        ImiPlayerInfo player = players[id];
                        if(player == null || !player.GetPlayerTracked())
                        {
                            continue;
                        }
                        if (!player.playerGestures.ContainsKey(gesture))
                        {
                            ImiGesture g = (ImiGesture)Activator.CreateInstance(gesture);
                            g.GestureEventListeners += listener;
                            player.playerGestures.Add(gesture, g);
                        }
                        else
                        {
                            player.playerGestures[gesture].GestureEventListeners = listener;
                        }
                    }
                }
            }
        }

        private void updateMainPlayerGesturePro(ImiGesturePro.GestureType gestureType, ImiGesture.OnGestureEvent listener)
        {
            int mainPlayerID = ImiManager.GetInstance().GetControlPlayerId();
            ImiPlayerInfo mainPlayer = ImiManager.GetInstance().GetPlayerInfo(mainPlayerID);
            if (mainPlayer != null && mainPlayer.GetPlayerTracked())
            {
                if (!mainPlayer.playerGesturesPro.ContainsKey(gestureType))
                {
                    ImiGesture.OnGestureEvent listeners = new ImiGesture.OnGestureEvent(listener);
                    mainPlayer.playerGesturesPro.Add(gestureType, listeners);
                }
                else
                {
                    mainPlayer.playerGesturesPro[gestureType] = listener;
                }
                AddGesturePro(mainPlayerID, gestureType);
            }
        }

        private void updateSubPlayerGesturePro(ImiGesturePro.GestureType gestureType, ImiGesture.OnGestureEvent listener)
        {
            Dictionary<int, ImiPlayerInfo> players = ImiManager.GetInstance().GetPlayerInfos();
            int mainPlayerID = ImiManager.GetInstance().GetControlPlayerId();
            if (players != null)
            {
                List<int> ids = new List<int>(players.Keys);
                foreach (int id in ids)
                {
                    if (id != mainPlayerID)
                    {
                        ImiPlayerInfo player = players[id];
                        if(player == null || !player.GetPlayerTracked())
                        {
                            continue;
                        }
                        if (!player.playerGesturesPro.ContainsKey(gestureType))
                        {
                            ImiGesture.OnGestureEvent listeners = new ImiGesture.OnGestureEvent(listener);
                            player.playerGesturesPro.Add(gestureType, listeners);
                        }
                        else
                        {
                            player.playerGesturesPro[gestureType] = listener;
                        }
                        AddGesturePro(id, gestureType);
                    }
                }
            }
        }

    }
}


